using System;
using NEnsure.Rules;
using MbUnit.Framework;
using Rhino.Mocks;

namespace NEnsure.UnitTests
{
	[TestFixture]
	public class CommonRuleTests
	{
		[RowTest]
		[Row("ab", true)]
		[Row("--", false)]
		[Row("---", true)]
		public void RegexRule_Should_Return_Broken_If_Value_Does_Not_Match_Pattern(string value, bool broken)
		{
			IBusinessRule<string> rule = new RegexRule(@"^[^a-zA-Z0-9]{2}$");

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[Test]
		[ExpectedArgumentNullException]
		public void RegexRule_Should_Die_If_Property_Name_Is_Nothing()
		{
			IBusinessRule<string> rule = new RegexRule(null);
		}

		[RowTest]
		[Row("123", false)]
		[Row("a123", false)]
		[Row("123a", false)]
		[Row("a-a", true)]
		[Row("1a-", true)]
		[Row("", false)]
		[Row("1111111111111111.11111111111111", true)]
		public void AlphaNumericRule_Should_Return_Broken_If_Value_Contains_Other_Than_Letters_And_Digits(string value, bool broken)
		{
			IBusinessRule<string> rule = new AlphaNumericRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("a", false)]
		[Row("", false)]
		[Row("1", true)]
		[Row("aa-", true)]
		public void AlphaOnlyRule_Should_Return_Broken_If_Value_Contains_Other_Than_Letters(string value, bool broken)
		{
			IBusinessRule<string> rule = new AlphaOnlyRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("a1", false)]
		[Row("1a", false)]
		[Row("1a1", false)]
		[Row("11", true)]
		[Row("a", false)]
		[Row(null, true)]
		public void ContainsAlphaRule_Should_Return_Broken_If_Value_Does_Not_Contain_Letter(string value, bool broken)
		{
			IBusinessRule<string> rule = new ContainsAlphaRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("1a", false)]
		[Row("a1", false)]
		[Row("a1a", false)]
		[Row("9", false)]
		[Row("aa", true)]
		[Row(null, true)]
		public void ContainsDigitRule_Should_Return_Broken_If_Value_Does_Not_Contain_Digit(string value, bool broken)
		{
			IBusinessRule<string> rule = new ContainsDigitRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("a@a.com", false)]
		[Row("ab@a-b.net", false)]
		[Row("abc", true)]
		[Row("abc@abc1", true)]
		[Row("ab@ab1-a.com", false)]
		[Row("a@a.info", false)]
		[Row("", false)]
		public void EmailRule_Should_Return_Broken_If_Value_Is_Not_Email(string value, bool broken)
		{
			IBusinessRule<string> rule = new EmailRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[Test]
		public void MaxLengthRule_Should_Return_Broken_If_Value_Is_More_Than_Max_Characters_Long()
		{
			IBusinessRule<string> rule = new MaxLengthRule(4);

			Assert.IsTrue(rule.IsBrokenBy("12345"));
			Assert.IsFalse(rule.IsBrokenBy("123"));
		}

		[Test]
		[ExpectedArgumentException]
		public void MaxLengthRule_Should_Die_If_MaxLength_Is_Less_Than_Zero()
		{
			IBusinessRule<string> rule = new MaxLengthRule(-1);
		}

		[Test]
		public void MaxValueRule_Should_Return_Broken_If_Value_Is_Greater_Than_Max_Value()
		{
			IBusinessRule<int> rule = new MaxValueRule<int>(10);

			Assert.IsTrue(rule.IsBrokenBy(11));
			Assert.IsFalse(rule.IsBrokenBy(8));
		}

		[Test]
		public void MinLengthRule_Should_Return_Broken_If_Value_Is_Less_Than_Min_Characters_Long()
		{
			IBusinessRule<string> rule = new MinLengthRule(4);

			Assert.IsFalse(rule.IsBrokenBy("12345"));
			Assert.IsTrue(rule.IsBrokenBy("123"));
		}

		[Test]
		[ExpectedArgumentException]
		public void MinLengthRule_Should_Die_If_MaxLength_Is_Less_Than_Zero()
		{
			IBusinessRule<string> rule = new MinLengthRule(-1);
		}

		[Test]
		public void MinValueRule_Should_Return_Broken_If_Value_Is_Less_Than_Min_Value()
		{
			IBusinessRule<int> rule = new MinValueRule<int>(10);

			Assert.IsFalse(rule.IsBrokenBy(11));
			Assert.IsTrue(rule.IsBrokenBy(8));
		}

		[RowTest]
		[Row("a", true)]
		[Row("", false)]
		[Row("1", false)]
		[Row("aa-", true)]
		[Row("11-", true)]
		public void NumericOnlyRule_Should_Return_Broken_If_Contains_Non_Digits(string value, bool broken)
		{
			IBusinessRule<string> rule = new NumericOnlyRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[Test]
		public void RequiredRule_Should_Return_Broken_If_String_Value_Is_Empty()
		{
			IBusinessRule<string> rule = new RequiredRule<string>();

			Assert.IsTrue(rule.IsBrokenBy(string.Empty));
			Assert.IsFalse(rule.IsBrokenBy("blah"));
		}

		[Test]
		public void RequiredRule_Should_Return_Broken_If_Value_Is_Nothing()
		{
			IBusinessRule<object> rule = new RequiredRule<object>();

			Assert.IsTrue(rule.IsBrokenBy(null));
			Assert.IsFalse(rule.IsBrokenBy(1));
		}

		[Test]
		public void RequiredRule_Should_Return_Broken_If_EmptyEquatable_Says_Empty()
		{
			MockRepository mocks = new MockRepository();

			IEmptyEquatable eq1 = mocks.StrictMock<IEmptyEquatable>();
			IEmptyEquatable eq2 = mocks.StrictMock<IEmptyEquatable>();

			SetupResult.For(eq1.AmIEmpty()).Return(true);
			SetupResult.For(eq2.AmIEmpty()).Return(false);

			mocks.ReplayAll();

			IBusinessRule<IEmptyEquatable> rule = new RequiredRule<IEmptyEquatable>();

			Assert.IsTrue(rule.IsBrokenBy(eq1));
			Assert.IsFalse(rule.IsBrokenBy(eq2));
		}

		[Test]
		public void RequiredRule_Should_Return_Broken_If_Value_Is_Default_For_Type()
		{
			IBusinessRule<double> rule = new RequiredRule<double>();
			IBusinessRule<DateTime> rule2 = new RequiredRule<DateTime>();

			Assert.IsTrue(rule.IsBrokenBy(0.0d));
			Assert.IsTrue(rule2.IsBrokenBy(DateTime.MinValue));
		}

		[RowTest]
		[Row("abc", true)]
		[Row("123-123-1234", true)]
		[Row("111-11-1111", false)]
		[Row("123456789", false)]
		[Row("123.45.6789", false)]
		[Row("1234.56.789", true)]
		[Row("abc.de.fghi", true)]
		[Row("123 .45. 6789", false)]
		[Row("", false)]
		public void SSNRule_Should_Return_Broken_If_Value_Is_Not_SSN(string value, bool broken)
		{
			IBusinessRule<string> rule = new SSNRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("abc", false)]
		[Row("123", false)]
		[Row("ab1", false)]
		[Row("a-", false)]
		[Row("%", true)]
		[Row("a b", true)]
		[Row("a$a", true)]
		[Row("", false)]
		public void UrlPartRule_Should_Return_Broken_If_Value_Cannot_Be_Part_Of_Url(string value, bool broken)
		{
			IBusinessRule<string> rule = new UrlPartRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("a b", true)]
		[Row("a'a", true)]
		[Row("a", false)]
		[Row("a*b", true)]
		[Row("a$a", false)]
		[Row("abcd,.%$ef1", false)]
		[Row("", false)]
		public void UsernameRule_Should_Return_Broken_If_Value_Is_Not_Valid_Username(string value, bool broken)
		{
			IBusinessRule<string> rule = new UsernameRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("1234567890", false)]
		[Row("123-456-7890", false)]
		[Row("123.456.7890", false)]
		[Row("123 456 7890", false)]
		[Row("1234567", false)]
		[Row("123-4567", false)]
		[Row("123-abc-1234", true)]
		[Row("12-3456-7890", true)]
		[Row("123$456$7890", true)]
		[Row("", false)]
		public void USPhoneRule_Should_Return_Broken_If_Value_Is_Not_Valid_US_Phone(string value, bool broken)
		{
			IBusinessRule<string> rule = new USPhoneRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[RowTest]
		[Row("12345-1234", false)]
		[Row("123451234", false)]
		[Row("12345", false)]
		[Row("12345-", true)]
		[Row("abc", true)]
		[Row("a1234-a234", true)]
		public void USZipCodeRule_Should_Return_Broken_If_Value_Is_Not_Valid_ZipCode(string value, bool broken)
		{
			IBusinessRule<string> rule = new USZipCodeRule();

			Assert.AreEqual(broken, rule.IsBrokenBy(value));
		}

		[Test]
		public void SameAsRule_Should_Return_Broken_If_Values_Are_Not_The_Same()
		{
			IBusinessRule<string> rule = new SameAsRule<string>("AnotherProperty", "Another Property Value");

			Assert.IsTrue(rule.IsBrokenBy("Different Value"));
		}

		[Test]
		public void SameAsRule_Should_Not_Return_Broken_If_Case_Is_Different_And_IgnoreCase()
		{
			IBusinessRule<string> rule = new SameAsRule("A", "A", true);

			Assert.IsFalse(rule.IsBrokenBy("a"));
		}

		[Test]
		public void EqualToRule_Should_Return_Broken_If_Values_Are_Not_Equal()
		{
			IBusinessRule<string> rule = new EqualToRule<string>("Value");

			Assert.IsTrue(rule.IsBrokenBy("Different value"));
			Assert.IsFalse(rule.IsBrokenBy("Value"));
		}

		[Test]
		public void UniqueRule_Should_Return_Broken_If_Value_IsInUse()
		{
			Predicate<string> isInUse = new Predicate<string>(delegate(string value) { return value == "existing"; });

			IBusinessRule<string> rule = new UniqueRule(isInUse);

			Assert.IsTrue(rule.IsBrokenBy("existing"));
			Assert.IsFalse(rule.IsBrokenBy("brand-new"));
		}
	}
}